package myJs.user.workflow.featureSelection

import japgolly.scalajs.react.vdom.all.cls
import myJs.Implicits.{MyTuple, _}
import myJs.api.Api
import myJs.cps._
import myJs.myPkg._
import myJs.myPkg.jquery._
import myJs.myPkg.reactHookForm._
import myJs.myPkg.topology._
import myJs.tool.Tool
import org.scalajs.dom.{Event, html, window}
import shared.pojo.Pojo.LoginUserData
import myJs.tool.Tool.messages
import shared.Pojo.MetDataKind

import scala.scalajs.js
import scala.scalajs.js.{JSON, UndefOr, |}
import shared.pojo.Pojo._

import scala.scalajs.js.Dynamic.{global => g}
import shared.pojo.Pojo.workflowMission._
import upickle.default._
import com.softwaremill.quicklens._
import japgolly.scalajs.react.facade.SyntheticDragEvent
import myJs.myPkg.contextMenu._
import myJs.myPkg.yup.Yup
import myJs.user.FeatureSelectionTool
import myJs.user.workflow.featureSelection.cor.Cor
import myJs.user.workflow.featureSelection.diagnoseAna.DiagnoseAna
import myJs.user.workflow.featureSelection.intersect.Intersect
import myJs.user.workflow.featureSelection.pathwayAna.PathwayAna
import myJs.user.workflow.featureSelection.pca.Pca
import myJs.user.workflow.featureSelection.uniAna.UniAna
import shared.pojo.GroupFileData.GetGroups

import scala.scalajs.js.JSConverters._
import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._
import shared.SharedTool
import shared.pojo.Data.{GetNumericColNames, GetRowCols}

import scala.collection.immutable.SeqMap
import japgolly.scalajs.react.vdom.all._
import japgolly.scalajs.react._
import japgolly.scalajs.react.React.Fragment
import japgolly.scalajs.react.util.EffectCatsEffect._
import myJs.myPkg.Hooks._
import cats.effect._
import cats.effect.unsafe.implicits.global
import myJs.Implicits._
import myJs.Utils._
import cats.syntax.all._

/** Created by yz on 3/11/2020
  */
object Cp {
  var count = 1
  var topology: Topology = _
  case class Props(missionName: String) {
    @inline def render: VdomElement = {
      Component(this)
    }
  }

  val curTool = FeatureSelection

  val Component = ScalaFnComponent[Props] { props =>
    val useForm = ReactHookForm.useForm(
      UseFormOptions(mode = "onChange", resolver = yupResolver(Schema.schema))
    )

    val (softKind, setSoftKind) = useState("featureSelection")
    val (fsData, setFsData) = useState(FsData(missionName = props.missionName))
    val (extraData, setExtraData) = useState(ExtraData())
    val (showIdStr, setShowIdStr) = useState("")
    val (selectedPenIds, setSelectedPenIds) = useState(List[String]())
    val (draggable, setDraggable) = useState(false)
    val (canDelete, setCanDelete) = useState(false)
    val (canCopy, setCanCopy) = useState(false)
    val (canReset, setCanReset) = useState(false)
    val (canRefreshGroups, setCanRefreshGroups) = useState(false)

    val fieldArrayMap = AllFSSoft.fieldArraySofts.map { x =>
      (x.nameStr, x.fieldArray(useForm))
    }.toSeqMap

    object FTool {

      def fsDataChange = (newData: FsData) => {
        setFsData(newData)
      }

      def fsDataModifyF = (newData: FsData => FsData) => {
        setFsData(newData)
      }

      def fileSelectShowChange = (v: Boolean) => {
        val newData = extraData.modify(_.fileSelectShow).setTo(v)
        setExtraData(newData)
      }

      def extraDataChange = (v: ExtraData) => {
        setExtraData(v)
      }

      def fileSelect = (v: String) =>
        {
          SyncIO {
            useForm.setValue("file", v)
            useForm.setValue("groupFile", "")
          } >>
            FTool.fileSelectShowChange(false) >>
            FTool.fileChange(v)
        }.unsafeRunSync()

      def groupFileSelect = (v: String) =>
        {
          useForm.setValue("groupFile", v)
          FTool.fileSelectShowChange(false) >>
            FTool.groupFileChange(v) >> {
              if (js.isUndefined(topology.find("dataFileNode"))) {
                refreshDataFileNode
              } else SyncIO.unit
            }
        }.unsafeRunSync()

      def fileSelectShow = {
        val newData = extraData
          .modify(_.tabs)
          .setTo(List(MetDataKind.name, "example"))
          .modify(_.fileSelectShow)
          .setTo(true)
          .modify(_.exampleFileName)
          .setTo("data.txt")
          .modify(_.fileSelect)
          .setTo(FTool.fileSelect)
        setExtraData(newData)
      }

      def groupFileSelectShow = {
        if (fsData.file.isEmpty) SyncIO {
          useForm.trigger("file")
        }
        else {
          val newRequest = groupFileData.GetAll.Request(
            id = curTool.getId,
            fileName = fsData.file
          )
          val newData = extraData
            .modify(_.tabs)
            .setTo(List("group", "example"))
            .modify(_.request)
            .setTo(newRequest)
            .modify(_.fileSelectShow)
            .setTo(true)
            .modify(_.exampleFileName)
            .setTo("group.txt")
            .modify(_.fileSelect)
            .setTo(FTool.groupFileSelect)
          setExtraData(newData)
        }
      }

      def refreshDataFileNode = {
        val index =
          layer.load(1, Tool.layerOptions.shade(js.Array("0.1", "#fff")))
        val dataFileRect = {
          val centerX = 300
          val centerY = 80
          val halfWidth = 60
          val halfHeight = 20
          Rect(
            x = centerX - halfWidth,
            y = centerY - halfHeight,
            width = halfWidth * 2,
            height = halfHeight * 2,
            center = Point(
              x = centerX,
              y = centerY
            ),
            ex = centerX + halfWidth,
            ey = centerY + halfHeight
          )
        }
        val dataFileNode = TopologyNode(
          id = "dataFileNode",
          name = "rectangle",
          borderRadius = 0.1,
          paddingBottom = 10,
          paddingTop = 10,
          paddingLeft = 10,
          paddingRight = 10,
          text = messages("dataMatrix"),
          rect = dataFileRect,
          fullTextRect = dataFileRect,
          hideAnchor = true
        )
        topology.addNode(dataFileNode, focus = true)
        val preprocessRect = {
          val centerX = 300
          val centerY = 150
          val halfWidth = 60
          val halfHeight = 20
          Rect(
            x = centerX - halfWidth,
            y = centerY - halfHeight,
            width = halfWidth * 2,
            height = halfHeight * 2,
            center = Point(
              x = centerX,
              y = centerY
            ),
            ex = centerX + halfWidth,
            ey = centerY + halfHeight
          )
        }
        val preprocessNode = TopologyNode(
          id = "preprocess_0",
          name = "rectangle",
          borderRadius = 0.1,
          paddingBottom = 10,
          paddingTop = 10,
          paddingLeft = 10,
          paddingRight = 10,
          text = messages("cleaningAndProcessing"),
          rect = preprocessRect,
          fullTextRect = preprocessRect,
          anchorType = AnchorType.UpAndBottom
        )
        topology.addNode(preprocessNode)
        val line = Line(
          from = Point(
            x = 300,
            y = 100,
            direction = Direction.Bottom,
            anchorIndex = 0,
            id = "dataFileNode"
          ),
          to = Point(
            x = 300,
            y = 130,
            direction = Direction.Up,
            anchorIndex = 0,
            id = "preprocess_0",
            autoAnchor = false
          ),
          name = "curve",
          controlPoints = js.Array(
            Point(
              x = 300,
              y = 110,
              direction = Direction.Bottom,
              anchorIndex = 0,
              id = "dataFileNode"
            ),
            Point(
              x = 300,
              y = 120,
              direction = Direction.Up,
              anchorIndex = 0,
              id = "1"
            )
          ),
          toArrow = "triangleSolid",
          locked = Lock.NoEvent
        )
        topology.addLine(line)
        topology.render()
        setShowIdStr(dataFileNode.id)
        layer.close(index)
        setDraggable(true)
      }

      def getGroups = {
        val request =
          GetGroups.Request(file = fsData.file, groupFile = fsData.groupFile)
        Api.groupFileData.getGroups(request).map { response =>
          response
        }
      }

      def refreshExtraData = {
        (FTool.getGroups, FTool.getRowNames, FTool.getGroupHeaders).tupled.flatMapSync {
          case (groups, rowNames, headers) =>
            val newExtraData = extraData
              .modify(_.groups)
              .setTo(groups)
              .modify(_.rowNames)
              .setTo(rowNames)
              .modify(_.groupHeaders)
              .setTo(headers)
            setExtraData(newExtraData)
        }
      }

      def getRowNames = {
        val request = GetRowCols.Request(fileName = fsData.file)
        Api.data.getRowCols(request).map { response =>
          response.rowNames
        }
      }

      def getGroupHeaders = {
        val request = GetNumericColNames.Request(fileName = fsData.groupFile)
        Api.data.getNumericColNames(request).map { response =>
          response.headers.filterNot { x =>
            List("Sample", "ClassNote", "Pair_ID").contains(x)
          }
        }
      }

      def myRun = {
        val pens = topology.data.pens
        val nodes = pens
          .filter { pen =>
            pen.`type` == PenType.Node && pen.id != "dataFileNode"
          }
          .map { pen =>
            pen.toNode
          }
        val interUnionNodes = nodes.filter { node =>
          node.id.startsWith("intersect") || node.id.startsWith("union")
        }
        if (nodes.isEmpty) IO {
          layer.msg(
            messages("ProcessesDoNotContainModules"),
            LayerOptions.icon(5).time(3000)
          )
        }
        else {
          var checkB = true
          val graph = curTool.getGraph(topology)
          val noInputNodeOp = nodes.find { node =>
            graph.find(node.id).isEmpty || graph.get(node.id).incoming.isEmpty
          }
          if (noInputNodeOp.isDefined) {
            layer.msg(
              s"${messages("moduleMustHasInput", noInputNodeOp.get.text)}",
              LayerOptions.icon(5).time(3000)
            )
            checkB = false
          }
          if (checkB) {
            val inputNotEnoughNodeOp = nodes
              .filter { node =>
                interUnionNodes.map(_.id).contains(node.id)
              }
              .find { node =>
                val curNode = graph.get(node.id)
                curNode.incoming.size < 2
              }
            if (inputNotEnoughNodeOp.isDefined) {
              val curNode = inputNotEnoughNodeOp.get
              layer.msg(
                s"${messages("moduleMustHas2Input", curNode.text)}",
                LayerOptions.icon(5).time(3000)
              )
              checkB = false
            }
          }
          if (checkB) {
            val existExample=List(fsData.file,fsData.groupFile).exists(_.startsWith("example"))
            val allIsExample=List(fsData.file,fsData.groupFile).forall(_.startsWith("example"))
            if (existExample && (!allIsExample)) {
              layer.msg(
                s"You can't select both the example file and your own file at the same time",
                LayerOptions.icon(5).time(5000)
              )
              checkB = false
            }
          }
          if (checkB) {
            val layerOptions = LayerOptions
              .title(Tool.zhInfo)
              .closeBtn(0)
              .skin("layui-layer-molv")
              .btn(js.Array())
            val index = layer.alert(Tool.myElement, layerOptions)
            val newData =
              fsData.modify(_.graphData).setTo(JSON.stringify(topology.data))
            Api.workflowMission.featureSelection(newData).map { response =>
              layer.close(index)
              if (response.valid) {
                window.location.href = g.jsRoutes.controllers.WorkflowController
                  .missionManageBefore()
                  .url
                  .toString
              } else {
                g.swal("Error", response.message, "error")
              }
            }
          } else IO.unit
        }
      }

      def missionNameChange = (v: String) => {
        val newData = fsData.modify(_.missionName).setTo(v)
        setFsData(newData)
      }

      def exampleWorkflowModifyF = (v: String) => {
        val modifyF = modify(_: FsData)(_.exampleWorkflow).setTo(v)
        FTool.fsDataModifyF(modifyF).map { x =>
          val io = if (fsData.exampleWorkflow.nonBlank) {
            Api.workflowMission.getConfigData(s"${v}.json").flatMapSync { tmpFsData =>
              FTool.refreshFromBeforeConfig(tmpFsData)
            }
          } else IO.unit
          io.unsafeRunAndForget()
        }

      }

      def fileChange = (v: String) => {
        val newData = fsData.modify(_.file).setTo(v)
        setFsData(newData)
      }

      def groupFileChange = (v: String) => {
        val newData = fsData.modify(_.groupFile).setTo(v)
        setCanRefreshGroups(true) >>
          setFsData(newData)
      }

      def dragOver(e: ReactEventFromHtml) = SyncIO {
        e.preventDefault()
      }

      def drop(e: ReactEventFromHtml) = {
        e.preventDefault()
        val ev = e.asInstanceOf[SyntheticDragEvent[html.Div]]
        val node = JSON
          .parse(ev.dataTransfer.getData("Topology"))
          .asInstanceOf[TopologyNode]
        val kind = SharedTool.getKind(node.id)
        val soft = AllFSSoft.kindSoftMap(kind)
        val curFieldArray = fieldArrayMap(kind)
        soft.appendData(
          curFieldArray,
          fsData,
          FTool.fsDataChange,
          node.id,
          extraData
        ) >>
          setCanDelete(true) >>
          setCanCopy(true) >>
          setSelectedPenIds(List(node.id)) >>
          setShowIdStr(node.id)
      }

      def hideMenu = SyncIO {
        ReactContextMenu.hideMenu()
      }

      def penDelete = SyncIO {
        val ids = curTool.getFieldArrayNodes(topology).map(_.id)
        val needDeletePenIds = selectedPenIds
          .filterNot { nodeId =>
            List("dataFileNode", "preprocess_0").contains(nodeId)
          }
        val needDeleteLines = needDeletePenIds
          .filter { penId =>
            topology.find(penId).toPen.`type` == PenType.Line
          }
          .map { penId =>
            topology.find(penId).toPen
          }
        val needDeleteNodes = needDeletePenIds
          .filter { penId =>
            topology
              .findOp(penId)
              .map { myPen =>
                myPen.toPen.`type` == PenType.Node
              }
              .getOrElse(false)
          }
          .map(topology.find(_).toPen.toNode)
        val idCurIMap = ids
          .map { id =>
            val kind = SharedTool.getKind(id)
            (kind, id)
          }.toList
          .groupSeqMap
          .toList
          .flatMap { case (kind, ids) =>
            ids
              .map { id =>
                val index = id.replaceAll(s"^[^_]+_", "").toInt
                (id, index)
              }
              .sortBy(_._2)
              .zipWithIndex
              .map { case ((id, index), curI) =>
                (id, curI)
              }
          }
          .toSeqMap
        val needDeletePens = (needDeleteLines ::: needDeleteNodes)
        topology.delete(needDeletePens)
        curTool.batchDeleteNodes(needDeleteNodes, idCurIMap, fsData, fsDataModifyF, fieldArrayMap)
      }.flatten

      def nodeCopy = SyncIO {
        val selectedNodeId = selectedPenIds.head
        val originalNode = topology.find(selectedNodeId).toPen.toNode
        val kind = SharedTool.getKind(selectedNodeId)
        val nodes = topology.data.pens
          .filter { pen =>
            pen.`type` == PenType.Node && pen.id.startsWith(kind)
          }
          .map(_.toNode)
          .toList
        val id = curTool.getNewNodeId(nodes)
        val node = curTool.getCopyNewNode(originalNode = originalNode, id = id)
        topology.addNode(node, focus = true)
        val curFieldArray = fieldArrayMap(kind)
        val curI = curTool.getCurI(nodes, originalNode)
        curTool.appendCopyData(fsData, curI, node.id, FTool.fsDataChange, curFieldArray) >>
          setSelectedPenIds(List(node.id)) >>
          setShowIdStr(node.id)
      }.flatten

      def refreshGroups = {
        val modifyF = modify(_: FsData)(_.dataMatrix.groups).setTo(extraData.groups)
        fsDataModifyF(modifyF)
      }

      def refreshFromBeforeConfig(tmpFsData: FsData) = {
        SyncIO {
          val curFsData =
            tmpFsData.modify(_.missionName).setTo(curTool.getMissionName)
          val graphDataDict = JSON.parse(curFsData.graphData).toJDJAn
          topology.open(graphDataDict)
          val dataFileNode = topology.find("dataFileNode").toPen
          curTool.addPen2ActiveLayer(topology, dataFileNode)
          (curFsData, dataFileNode)
        }.flatMap { case (curFsData, dataFileNode) =>
          setFsData(curFsData) >>
            setShowIdStr(dataFileNode.id) >>
            setCanReset(true) >>
            setCanRefreshGroups(false) >>
            setDraggable(true)
        }
      }

    }

    useEffect(
      {
        SyncIO {
          topology = new Topology(
            parent = "topo-canvas",
            options = TopologyOptions(
              hideRotateCP = true,
              hideSizeCP = true,
              disableScale = true,
              disableEmptyLine = true,
              autoAnchor = false,
              hideAnchor = false,
              rotateCursor = "pointer",
              hoverCursor = "pointer",
              disableDockLine = true,
              hideInput = true,
              anchorFillStyle = "red"
            )
          )
          if (Tool.getMissionId.isEmpty) {
            topology.open(js.Dictionary("grid" -> true))
          }
          topology.myOn(
            "space", {
              setCanDelete(false) >>
                setCanCopy(false)
            }
          )
          topology.myOn(
            "line",
            (pen: MyPen) => {
              setSelectedPenIds(List(pen.toPen.id)) >>
                setCanDelete(true)
            }
          )
          topology.myOn(
            "node",
            (pen: MyPen) => {
              val node = pen.toPen.toNode
              val notRemoveNodeIds = List("dataFileNode", "preprocess_0")
              val b = if (notRemoveNodeIds.contains(node.id)) {
                false
              } else true
              setCanDelete(b) >>
                setCanCopy(b) >>
                setShowIdStr(node.id) >>
                setSelectedPenIds(List(node.id))
            }
          )
          topology.myOn(
            "multi",
            (pen: MyPen) => {
              val ids = pen.toPens.toList.map(_.id)
              setSelectedPenIds(ids) >>
                setCanDelete(true)
            }
          )
          topology.myOn(
            "addLine",
            (myPen: MyPen) => {
              val line = myPen.toPen.toLine
              val dbLines = topology.data.pens
                .filter { pen =>
                  pen.`type` == PenType.Line
                }
                .filter { pen =>
                  val dbLine = pen.toLine
                  dbLine.from.id == line.from.id && dbLine.to.id == line.to.id
                }
              val isDelete = (line.from.id == line.to.id) || (dbLines.size > 1) ||
                (curTool.isOtherNode(line.to.id) && curTool
                  .otherNodeHasMoreInput(line.to.id, topology)) ||
                curTool.hasCycle(topology)
              if (isDelete) SyncIO {
                topology.delete()
              }
              else {
                setCanDelete(true)
              }
            }
          )
          topology.myOn(
            "delete",
            (myPen: MyPen) => {
              setCanDelete(false) >>
                setShowIdStr("") >>
                setCanCopy(false)
            }
          )
        }.to[IO] >> {
          if (Tool.getMissionId.nonBlank) {
            Api.workflowMission.configData(Tool.getMissionId).flatMapSync { tmpFsData =>
              FTool.refreshFromBeforeConfig(tmpFsData)
            }
          } else IO.unit
        }
      },
      List()
    )

    println(s"===render ${count}===")
    count += 1

    useEffect(
      {
        if (fsData.file.nonBlank && fsData.groupFile.nonBlank) {
          FTool.refreshExtraData
        } else IO.unit
      },
      List(fsData.file, fsData.groupFile)
    )

    useEffect(
      {
        if (canRefreshGroups) {
          FTool.refreshGroups
        } else SyncIO.unit
      },
      List(extraData.groups)
    )

    useEffect(
      {
        SyncIO {} >> {
          if (canReset && !canRefreshGroups) {
            useForm.reset(fsData)
            setCanReset(false)
          } else SyncIO.unit
        }

      },
      List(canReset, fsData, canRefreshGroups)
    )

    useForm.formState.errors.foreach { case (k, v) =>
      println(k, JSON.stringify(v))
    }

    val exampleWorkflowMap = SeqMap(
      "unselected" -> "--select--",
      "9" -> "#Demo for Workflow",
      "template_1" -> "靶向-2组-差异+通路",
      "template_2" -> "靶向-2组-差异+通路+诊断",
      "template_3" -> "靶向-多组-差异+通路",
      "template_4" -> "靶向-多组-差异+通路+诊断",
      "template_5" -> "非靶向-2组-差异+通路",
      "template_6" -> "非靶向-2组-差异+通路+诊断",
      "template_7" -> "非靶向-多组-差异+通路",
      "template_8" -> "非靶向-多组-差异+通路+诊断"
    )

    div(
      FormProvider(
        trigger = useForm.trigger _,
        control = useForm.control,
        formState = useForm.formState,
        setValue = useForm.setValue _,
        watch = useForm.watch _,
        getFieldState = useForm.getFieldState _
      )(
        form(
          className := "form-horizontal",
          marginTop := 30.px,
          onSubmit := {
            useForm.myHandleSubmit((data: js.Dictionary[js.Any]) => {
              FTool.myRun
            })
          },
          InputInlineCp
            .Props(
              name = "missionName",
              label = messages("taskName"),
              onChange = FTool.missionNameChange,
              value = fsData.missionName
            )
            .render,
          SelectInlineCp
            .Props(
              onChange = FTool.exampleWorkflowModifyF,
              value = fsData.exampleWorkflow,
              name = "exampleWorkflow",
              optionMap = exampleWorkflowMap,
              label = "Example workflow"
            )
            .render,
          FileSelectInlineCp
            .Props(
              fileSelectShow = FTool.fileSelectShow,
              label = messages("dataMatrix"),
              name = "file",
              value = fsData.file,
              onChange = FTool.fileChange
            )
            .render,
          FileSelectInlineCp
            .Props(
              fileSelectShow = FTool.groupFileSelectShow,
              label = messages("groupFile"),
              name = "groupFile",
              value = fsData.groupFile,
              onChange = FTool.groupFileChange
            )
            .render,
          div(
            className := "row",
            div(
              id := "left",
              className := "col-sm-1",
              paddingRight := "0px",
              paddingLeft := "0px",
              width := "11%",
              div(
                className := "canvas",
                float.left,
                width := "100%",
                div(
                  className := "unified-panel-header",
                  div(
                    className := "unified-panel-header-inner",
                    messages("modules")
                  )
                ),
                div(
                  className := "unified-panel-body",
                  div(
                    className := "toolMenu",
                    LeftModulesCp.Props(topology = topology, draggable = draggable).render
                  )
                ),
                div(
                  className := "unified-panel-footer",
                  div(
                    className := "panel-collapse",
                    span(className := "fa fa-chevron-left")
                  ),
                  div(className := "drag leftDrag")
                )
              ),
              div(className := "leftHandler")
            ),
            div(
              id := "center",
              className := "col-sm-11",
              paddingRight := "0px",
              paddingLeft := "0px",
              width := "68%",
              div(
                className := "unified-panel-header",
                div(
                  className := "unified-panel-header-inner",
                  float.right,
                  button(
                    `type` := "button",
                    title := s"${messages("delete")}",
                    disabled := (!canDelete),
                    onClick --> FTool.penDelete,
                    border := "0px",
                    className := "panel-header-button myRemoveButton",
                    span(className := "fa fa-trash")
                  )
                ),
                div(
                  className := "unified-panel-header-inner",
                  messages("workflow")
                )
              ),
              ContextMenuTrigger(
                id = "topologyMenu"
              )(
                div(
                  id := "topo-canvas",
                  onClick --> FTool.hideMenu,
                  onDragOver ==> FTool.dragOver,
                  onDrop ==> FTool.drop,
                  className := "canvas"
                )
              ),
              ContextMenu(
                id = "topologyMenu"
              )(
                MenuItem(
                  onClick = FTool.penDelete,
                  disabled = (!canDelete)
                )(
                  messages("delete")
                ),
                MenuItem(
                  onClick = FTool.nodeCopy,
                  disabled = (!canCopy)
                )(
                  messages("copy")
                )
              )
            ),
            div(
              id := "right",
              className := "col-sm-1",
              paddingRight := "0px",
              paddingLeft := "0px",
              width := "20%",
              div(className := "rightHandler"),
              div(
                className := "canvas",
                float.left,
                width := "100%",
                div(
                  className := "unified-panel-header",
                  div(
                    className := "unified-panel-header-inner",
                    messages("parameters")
                  )
                ),
                div(
                  className := "unified-panel-body",
                  div(
                    className := "toolMenu",
                    marginTop := "15px",
                    div(
                      id := "args",
                      dataMatrix.Cp
                        .Props(
                          fsData = fsData,
                          fsDataModifyF = FTool.fsDataModifyF,
                          showIdStr = showIdStr,
                          extraData = extraData
                        )
                        .render,
                      preprocess.Cp
                        .Props(
                          fsData = fsData,
                          fsDataChange = FTool.fsDataChange,
                          showIdStr = showIdStr,
                          extraData = extraData
                        )
                        .render,
                      AllFSSoft.fieldArraySofts.toVdomArray { soft =>
                        div(
                          key := s"${soft.nameStr}",
                          soft.cp(
                            fieldArray = fieldArrayMap(soft.nameStr),
                            fsData = fsData,
                            fsDataChange = FTool.fsDataChange,
                            fsDataModifyF = FTool.fsDataModifyF,
                            showIdStr = showIdStr,
                            extraData = extraData,
                            extraDataChange = FTool.extraDataChange
                          )
                        )
                      }
                    )
                  )
                ),
                div(
                  className := "unified-panel-footer",
                  div(
                    className := "panel-collapse right",
                    span(className := "fa fa-chevron-right")
                  ),
                  div(className := "drag rightDrag")
                )
              ),
              div(className := "leftHandler")
            )
          ),
          FileModalCp
            .Props(
              show = extraData.fileSelectShow,
              showChange = FTool.fileSelectShowChange,
              softKind = softKind,
              exampleFileName = extraData.exampleFileName,
              fileSelect = (v: String) => SyncIO { extraData.fileSelect(v) },
              request = extraData.request,
              tabs = extraData.tabs
            )
            .render,
          div(
            className := "form-group form-horizontal",
            marginTop := "20px",
            div(
              className := "actions col-sm-offset-2 col-sm-2",
              button(
                `type` := "submit",
                className := "btn btn-primary",
                width := "90%",
                messages("run")
              )
            )
          )
        )
      )
    )

  }

}
